package com.sky.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.entity.*;
import com.sky.exception.AddressBookBusinessException;
import com.sky.exception.OrderBusinessException;
import com.sky.exception.ShoppingCartBusinessException;
import com.sky.mapper.*;
import com.sky.query.OrderQuery;
import com.sky.service.OrdersService;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 马毅爽
 * @description 针对表【orders(订单表)】的数据库操作Service实现
 * @createDate 2023-11-09 20:56:39
 */
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders>
        implements OrdersService {

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WeChatPayUtil weChatPayUtil;
    @Autowired
    private WebSocketServer webSocketServer;

    @Override
    @Transactional
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        //处理各种业务异常
        AddressBook addressBook = addressBookMapper.selectById(ordersSubmitDTO.getAddressBookId());
        if (addressBook == null) {
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }

        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = ShoppingCart.builder().userId(userId).build();
        List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);

        if (list == null || list.isEmpty()) {
            throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        //向订单表插入一条数据
        Orders order = BeanUtil.copyProperties(ordersSubmitDTO, Orders.class);
        order.setOrderTime(LocalDateTime.now());
        //TODO 没有配置支付接口，这里暂时跳过支付
        order.setPayStatus(Orders.PAID);
        order.setStatus(Orders.TO_BE_CONFIRMED);
        order.setCheckoutTime(LocalDateTime.now().plusSeconds(10));//暂时
        order.setNumber(String.valueOf(System.currentTimeMillis()));
        order.setPhone(addressBook.getPhone());
        order.setConsignee(addressBook.getConsignee());
        order.setAddress(addressBookMapper.selectById(ordersSubmitDTO.getAddressBookId()).toAddress());
        order.setUserId(userId);

        save(order);

        //向订单明细表插入n条数据
        List<OrderDetail> orderDetails = new ArrayList<>();
        list.forEach(cart -> {
            OrderDetail orderDetail = BeanUtil.copyProperties(cart, OrderDetail.class);
            orderDetail.setOrderId(order.getId());
            orderDetails.add(orderDetail);
        });
        orderDetailMapper.insertBatch(orderDetails);
        //清空当前用户的购物车数据
        shoppingCartMapper.removeByUserId(userId);

        //通过websocket向客户端浏览器推送消息
        Map<String,Object> map = new HashMap<>();
        map.put("type",1);
        map.put("orderId",order.getId());
        map.put("content","订单号" + order.getNumber());

        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);
        //封装VO返回
        return OrderSubmitVO.builder()
                .id(order.getId())
                .orderTime(order.getOrderTime())
                .orderNumber(order.getNumber())
                .orderAmount(order.getAmount())
                .build();
    }

    @Override
    public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectById(userId);

        //调用微信支付接口，生成预支付交易单
        JSONObject jsonObject = weChatPayUtil.pay(
                ordersPaymentDTO.getOrderNumber(), //商户订单号
                new BigDecimal(0.01), //支付金额，单位 元
                "苍穹外卖订单", //商品描述
                user.getOpenid() //微信用户的openid
        );

        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
            throw new OrderBusinessException("该订单已支付");
        }

        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
        vo.setPackageStr(jsonObject.getString("package"));

        return vo;
    }

    @Override
    public void paySuccess(String outTradeNo) {

        // 根据订单号查询订单
        Orders ordersDB = ordersMapper.getByNumber(outTradeNo);

        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        ordersMapper.updateById(orders);
    }

    @Override
    public PageDTO<OrderVO> queryHistoryOrders(OrderQuery query) {
        Integer status = query.getStatus();
        Page<Orders> page = query.sortBy("order_time", false);
        Page<Orders> p = lambdaQuery()
                .eq(status != null, Orders::getStatus, status)
                .eq(Orders::getUserId, BaseContext.getCurrentId())
                .page(page);

        return PageDTO.of(p, orders -> {
            OrderVO orderVO = BeanUtil.copyProperties(orders, OrderVO.class);
            List<OrderDetail> list = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orders.getId()));
            orderVO.setOrderDetailList(list);
            return orderVO;
        });
    }

    @Override
    public PageDTO<OrderVO> conditionSearch(OrderQuery query) {
        Integer status = query.getStatus();
        String number = query.getNumber();
        String phone = query.getPhone();
        LocalDateTime beginTime = query.getBeginTime();
        LocalDateTime endTime = query.getEndTime();
        Page<Orders> page = query.sortBy("order_time", false);

        Page<Orders> p = lambdaQuery()
                .eq(status != null, Orders::getStatus, status)
                .eq(number != null, Orders::getNumber, "1699615375305")
                .like(phone != null, Orders::getPhone, phone)
                .ge(beginTime != null && endTime != null, Orders::getOrderTime, beginTime)
                .lt(beginTime != null && endTime != null, Orders::getOrderTime, endTime)
                .page(page);

        return PageDTO.of(p, orders -> {
            OrderVO orderVO = BeanUtil.copyProperties(orders, OrderVO.class);
            List<OrderDetail> list = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orders.getId()));

            orderVO.setOrderDishes(getOrderDishes(list));
            return orderVO;
        });
    }

    @Override
    public OrderStatisticsVO countStatus() {
        Integer toBeConfirmed = Math.toIntExact(ordersMapper.selectCount(new LambdaQueryWrapper<Orders>().eq(Orders::getStatus, Orders.TO_BE_CONFIRMED)));
        Integer confirmed = Math.toIntExact(ordersMapper.selectCount(new LambdaQueryWrapper<Orders>().eq(Orders::getStatus, Orders.CONFIRMED)));
        Integer deliveryInProgress = Math.toIntExact(ordersMapper.selectCount(new LambdaQueryWrapper<Orders>().eq(Orders::getStatus, Orders.DELIVERY_IN_PROGRESS)));
        return OrderStatisticsVO.builder()
                .toBeConfirmed(toBeConfirmed)
                .confirmed(confirmed)
                .deliveryInProgress(deliveryInProgress)
                .build();
    }

    @Override
    public void confirmOrder(OrdersConfirmDTO orders) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.CONFIRMED)
                .eq(Orders::getId, orders.getId())
                .update();
    }

    @Override
    public void deliveryOrder(Long id) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.DELIVERY_IN_PROGRESS)
                .set(Orders::getEstimatedDeliveryTime, LocalDateTime.now().plusMinutes(45))
                .eq(Orders::getId, id)
                .update();
    }

    @Override
    public void completeOrder(Long id) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.COMPLETED)
                .set(Orders::getDeliveryTime, LocalDateTime.now())
                .eq(Orders::getId, id)
                .update();
    }

    @Override
    public void cancelOrderAdmin(OrdersCancelDTO cancelDTO) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.CANCELLED)
                .set(Orders::getCancelTime, LocalDateTime.now())
                .set(Orders::getCancelReason,cancelDTO.getCancelReason())
                .eq(Orders::getId, cancelDTO.getId())
                .update();
    }

    @Override
    public void rejectOrder(OrdersRejectionDTO rejectionDTO) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.CANCELLED)
                .set(Orders::getCancelTime, LocalDateTime.now())
                .set(Orders::getCancelReason,rejectionDTO.getRejectionReason())
                .eq(Orders::getId, rejectionDTO.getId())
                .update();
    }

    @Override
    public OrderVO queryOrderDetails(Long id) {
        OrderVO orderVO = BeanUtil.copyProperties(getById(id),OrderVO.class);
        List<OrderDetail> list = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId,id));
        orderVO.setOrderDetailList(list);
        orderVO.setOrderDishes(getOrderDishes(list));
        return orderVO;
    }

    @Override
    public void remindOrder(Long id) {
        Orders orders = getById(id);
        if (orders == null){
            throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);
        }

        Map<String,Object> map = new HashMap<>();
        map.put("type",2);
        map.put("orderId",id);
        map.put("content","订单号"+orders.getNumber());

        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);
    }

    @Override
    public OrderVO orderDetail(Long id) {
        Orders orders = getById(id);
        OrderVO orderVO = BeanUtil.copyProperties(orders, OrderVO.class);
        List<OrderDetail> list = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, id));
        AddressBook addressBook = addressBookMapper.selectById(orderVO.getAddressBookId());

        String name = orderVO.getConsignee();
        String phone = orderVO.getPhone();
        name = name.charAt(0) + "**";
        phone = phone.substring(0, 3) + "****" + phone.substring(7);
        orderVO.setConsignee(name);
        orderVO.setPhone(phone);


        orderVO.setAddress(addressBook.getDetail());
        orderVO.setOrderDetailList(list);
        return orderVO;
    }


    @Override
    public void cancelOrder(Long id) {
        lambdaUpdate()
                .set(Orders::getStatus, Orders.CANCELLED)
                .set(Orders::getCancelTime, LocalDateTime.now())
                .set(Orders::getCancelReason, "用户取消")
                .eq(Orders::getId, id)
                .update();
    }

    @Override
    public void repetition(Long id) {
        List<OrderDetail> list = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, id));

        List<ShoppingCart> shoppingCarts = new ArrayList<>();

        list.forEach(orderDetail -> {
            ShoppingCart cart = BeanUtil.copyProperties(orderDetail, ShoppingCart.class);
            cart.setUserId(BaseContext.getCurrentId());
            cart.setCreateTime(LocalDateTime.now());
            shoppingCarts.add(cart);
        });

        shoppingCartMapper.insertBatch(shoppingCarts);
    }

    private String getOrderDishes(List<OrderDetail> list){
        StringBuilder orderDishes = new StringBuilder();
        list.forEach(orderDetail -> {
            String orderDish = orderDetail.getName() + "*" + orderDetail.getNumber() + ";";
            orderDishes.append(orderDish);
        });
        return orderDishes.toString();
    }


}




